/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-28 15:33:35
 * @LastEditTime: 2021-08-04 08:30:32
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include <string.h>
#include "sdkconfig.h"
#include "parameters.h"
#include "ft_assert.h"
#include "dw_i2c.h"
#include "dw_i2c_hw.h"
#include "eeprom.h"
#include "fsleep.h"

#define EEPROM_LEN  128

typedef struct
{
    I2cCtrl master;
    u8      buf[EEPROM_LEN];
    u32     bufIdx;
    u32     bufLen;
} I2cEepromMaster;

I2cEepromMaster masterEeprom;

static u32 I2cEeWriteRaw(I2cCtrl *pCtrl, I2cTransPack *pack)
{
    FT_ASSERTZERONUM(pCtrl && pack);
    u32 ret = I2C_SUCCESS;
    
    if (pack->bufLen > AT24C02_BLK_SIZE)
    {
        return I2C_ERR_NOT_SUPPORT;
    }

    ret = I2cMasterWrite(pCtrl, pack);
    fsleep_millisec(200); /* wait write finish */
    return ret;
}

void I2cEeMasterReadDoneCB(void *pPara)
{
    FT_ASSERTVOID(pPara);
    I2cCtrl *pCtrl = (I2cCtrl *)pPara;
    FT_ASSERTVOID(pCtrl->prv);
    I2cEepromMaster *pEeprom = (I2cEepromMaster *)pCtrl->prv;
    s32 rxValid;

    I2C_INFO("Master Read Done");

    rxValid = I2C_READ_REG32(pCtrl, I2C_RXFLR_OFFSET);
    while ((pEeprom->bufIdx < pEeprom->bufLen) && (rxValid > 0))
    {
        pEeprom->buf[pEeprom->bufIdx] = (I2C_DATA_MASK & I2C_READ_DATCMD(pCtrl));
        pEeprom->bufIdx++;
        rxValid--;
        fsleep_millisec(2);

        if (pEeprom->bufIdx != pEeprom->bufLen)
        {
            I2C_WRITE_DATCMD(pCtrl, I2C_DATA_CMD_RESTART | I2C_DATA_CMD_READ);
        }
        else
        {
            I2C_WRITE_DATCMD(pCtrl, I2C_DATA_CMD_STOP | I2C_DATA_CMD_READ);
            fsleep_millisec(2);
            I2cSetIntrruptMask(pCtrl, I2C_INTR_RX_FULL, FALSE);
        }
    }
}

void I2cEeMasterWriteDoneCB(void *pPara)
{
    FT_ASSERTVOID(pPara);
    I2cCtrl *pCtrl = (I2cCtrl *)pPara;
    FT_ASSERTVOID(pCtrl->prv);
    I2cEepromMaster *pEeprom = (I2cEepromMaster *)pCtrl->prv;

    I2C_INFO("Write Done");

    if (pEeprom->bufIdx == pEeprom->bufLen)
    {
        I2C_WRITE_DATCMD(pCtrl, I2C_DATA_CMD_STOP);
        fsleep_millisec(2);
        I2cSetIntrruptMask(pCtrl, I2C_INTR_TX_EMPTY, FALSE);
    }
    else
    {
        I2C_WRITE_DATCMD(pCtrl, (I2C_DATA_MASK & pEeprom->buf[pEeprom->bufIdx]));
        fsleep_millisec(2);
        pEeprom->bufIdx++;
    }    
}

static u32 I2cEeReadRaw(I2cCtrl *pCtrl, I2cTransPack *pack)
{
    return I2cMasterRead(pCtrl, pack);
}

u32 I2cEeMasterWrite(u32 slaveAddr, u8 offset, u8 *pBuf, u32 bufLen)
{
    I2cEepromMaster *pEeprom = &masterEeprom;
    I2cCtrl *pCtrl = &pEeprom->master;
    I2cTransPack pack;
    u32 ret = I2C_SUCCESS;
    const u8 transSize = AT24C02_BLK_SIZE;
    const u32 transTimes = bufLen / transSize;
    const u32 lastTransLen = bufLen % transSize;
    u8 loop;
    pCtrl->prv = pEeprom;

    memset(&pack, 0, sizeof(pack));
    pack.slaveAddr = slaveAddr;
    pack.addrLen = 1; /* use u8 offset */
    
    for (loop = 0; loop < transTimes; loop++)
    {
        pack.transBuf = pBuf + loop * transSize;
        pack.bufLen = transSize;
        pack.inChipAddr = offset + loop * transSize;
        I2C_INFO("write %d buf 0x%x len 0x%x", loop, pack.transBuf, pack.bufLen);
        ret = I2cEeWriteRaw(pCtrl, &pack);

        if (I2C_SUCCESS != ret)
            return ret;
    }

    if (0 != lastTransLen)
    {
        pack.transBuf = pBuf + transTimes * transSize;
        pack.bufLen = lastTransLen;
        pack.inChipAddr = offset + transTimes * transSize;
        I2C_INFO("write last buf 0x%x len 0x%x", pack.transBuf, pack.bufLen);
        ret = I2cEeWriteRaw(pCtrl, &pack);
    }

    return ret;
}

u32 I2cEeMasterRead(u32 slaveAddr, u8 offset, u8 *pBuf, u32 bufLen)
{
    I2cEepromMaster *pEeprom = &masterEeprom;
    I2cCtrl *pCtrl = &pEeprom->master;
    I2cTransPack pack;
    u32 ret = I2C_SUCCESS;
    const u8 transSize = bufLen; //6; //4; //AT24C02_BLK_SIZE - 1;
    const u32 transTimes = bufLen / transSize;
    const u32 lastTransLen = bufLen % transSize;
    u8 loop;
    pCtrl->prv = pEeprom;

    memset(&pack, 0, sizeof(pack));    
    pack.slaveAddr = slaveAddr;
    pack.addrLen = 1; /* use u8 offset */

    for (loop = 0; loop < transTimes; loop++)
    {
        pack.transBuf = pBuf + loop * transSize;
        pack.bufLen = transSize;
        pack.inChipAddr = offset + loop * transSize;         

        ret = I2cEeReadRaw(pCtrl, &pack);
        if (I2C_SUCCESS != ret)
            return ret;   
    }

    if (0 != lastTransLen)
    {
        pack.transBuf = pBuf + transTimes * transSize;
        pack.bufLen = lastTransLen;
        pack.inChipAddr = offset + transTimes * transSize;
        ret = I2cEeReadRaw(pCtrl, &pack);
    }
    
    return ret;
}

u32 I2cEeMasterInit()
{
    I2cEepromMaster *pEeprom = &masterEeprom;
    I2cCtrl *pCtrl = &pEeprom->master;
    u32 i2cId = I2C_INSTANCE_0;
    u32 ret = I2C_SUCCESS;

    pEeprom->bufIdx = 0;
    pEeprom->bufLen = EEPROM_LEN;

    memset(pCtrl, 0, sizeof(I2cCtrl));
    pCtrl->config = *I2cLookupConfig(i2cId);
    ret = I2cInit(pCtrl, &pCtrl->config);
    I2C_INFO("i2c init result : 0x%x", ret);
    if (I2C_SUCCESS != ret)
        return ret;

    pCtrl->prv = pEeprom;

    I2cRegisterEvtCallback(pCtrl, I2C_EVT_MASTER_READ_DONE, I2cEeMasterReadDoneCB);
    I2cRegisterEvtCallback(pCtrl, I2C_EVT_MASTER_WRITE_DONE, I2cEeMasterWriteDoneCB);

    return ret;
}